<style lang="scss">
  .ua-file {
    width: 100%;
    height: 120px;
    display: flex;
    &--select {
      width: 150px;
      height: 100%;
      position: relative;
      display: inline-block;
      border: 1px dashed #e2e2e2;
      text-align: center;
      padding: 20px;
      box-sizing: border-box;
      cursor: pointer;
      flex-shrink: 0;
      & > input[type='file'] {
        display: inline-block;
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        left: 0;
        opacity: 0;
        cursor: pointer;
      }
      &-icon {
        & > span {
          display: inline-block;
          width: 100%;
          text-align: center;
          color: #999;
          font-size: 12px;
          line-height: 1.5;
        }
      }
    }
    &--preview {
      flex-grow: 1;
      height: 100%;
      overflow: hidden;
      &-item {
        display: inline-block;
        height: 100%;
        margin-left: 10px;
      }
    }
  }
</style>

<template>
  <div class="ua-file">
    <div class="ua-file--select">
      <input type="file" ref="input" :multiple="multiple" :accept="accept" :disabled="!!count" @change="inputChange($event)" />
      <div class="ua-file--select-icon">
        <UIconfont name="upload-fill" :size="36" color="#009688" />
        <span>点击上传，或将文件拖拽到此处</span>
      </div>
    </div>
    <div class="ua-file--preview">
      <template v-for="item in views" :key="item.base64">
        <div class="ua-file--preview-item">
          <UImage v-if="item.type === 'image'" :src="item.source" fit="contain" :height="120" />
          <UAudio
            v-if="item.type === 'audio'"
            play-icon-color="#009688"
            :width="150"
            :height="120"
            :play-icon-size="50"
            :src="item.source"
            :no-poster="true"
          />
        </div>
      </template>
    </div>
  </div>
</template>

<script lang="ts">
  import { fileDetail, fileRead } from '@assets/utils/util'
  import { defineComponent, PropType, reactive, ref, watch } from 'vue'
  import { qiniuFullLink } from '@assets/utils/qiniu'
  import UImage from './UImage.vue'
  import UIconfont from './UIconfont.vue'
  import UAudio from './UAudio.vue'

  type FileType = 'image' | 'video' | 'audio'
  interface FileView {
    type: FileType
    source: string
  }

  export default defineComponent({
    name: 'FileSelect',
    components: { UImage, UIconfont, UAudio },
    props: {
      multiple: Boolean,
      accept: String,
      upload: {
        type: Function as PropType<(files: File[]) => Promise<void>>,
      },
      preview: {
        type: [String, Array] as PropType<string | string[]>,
        default: '',
      },
      required: {
        type: Boolean,
        default: false,
      },
      error: {
        type: String,
        default: '图片不能为空',
      },
    },
    setup(props) {
      /** input[file].files */
      const files = reactive<File[]>([])
      /** 多媒体预览列表 */
      const views = reactive<FileView[]>([])
      /** 等待转换的 input[file].files 文件个数，转换过程中禁止选择文件 */
      const count = ref(0)
      watch(() => props.preview, previewProp, { immediate: true })
      function previewProp(preview: string | string[]) {
        if (!preview) return
        files.length = 0
        views.length = 0
        if (!Array.isArray(preview)) {
          preview = [preview]
        }
        preview.forEach((tar: string) => {
          const { mime } = fileDetail(tar)
          views.push({
            type: mime.split('/').shift() as FileType,
            source: qiniuFullLink(tar),
          })
        })
      }
      /**
       * input[file].onchange
       */
      function inputChange(event: Event) {
        const temp = Array.from((event.target as HTMLInputElement).files!)
        if (temp.length) {
          count.value = temp.length
          files.length = 0
          views.length = 0
          temp.forEach((file) => {
            fileRead(file, 'base64').then(
              (data) => {
                files.push(file)
                views.push({
                  type: file.type.split('/').shift() as FileType,
                  source: data,
                })
                count.value--
              },
              () => {
                count.value--
              }
            )
          })
        } else {
          previewProp(props.preview)
        }
      }
      /**
       * 执行文件上传
       */
      function submit() {
        return new Promise<void>((resolve, reject) => {
          if (!files.length) {
            props.required && !props.preview ? reject(new Error(props.error)) : resolve()
          } else if (typeof props.upload === 'function') {
            props.upload(files).then(resolve, reject)
          } else {
            reject(new Error('未定义文件上传函数！！！'))
          }
        })
      }
      return { count, files, views, inputChange, submit }
    },
  })
</script>
